// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_TOOLS_FLIP_SERVER_SM_CONNECTION_H_
#define NET_TOOLS_FLIP_SERVER_SM_CONNECTION_H_

#include <arpa/inet.h> // in_addr_t
#include <stddef.h>
#include <time.h>

#include <list>
#include <string>

#include "base/compiler_specific.h"
#include "net/spdy/spdy_protocol.h"
#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/flip_server/mem_cache.h"
#include "net/tools/flip_server/ring_buffer.h"
#include "net/tools/flip_server/sm_interface.h"
#include "openssl/ssl.h"

namespace net {

class FlipAcceptor;
class MemoryCache;
struct SSLState;
class SpdySM;

// A frame of data to be sent.
class DataFrame {
public:
    const char* data;
    size_t size;
    bool delete_when_done;
    size_t index;
    DataFrame()
        : data(NULL)
        , size(0)
        , delete_when_done(false)
        , index(0)
    {
    }
    virtual ~DataFrame();
};

typedef std::list<DataFrame*> OutputList;

class SMConnection : public SMConnectionInterface,
                     public EpollCallbackInterface,
                     public NotifierInterface {
public:
    ~SMConnection() override;

    static SMConnection* NewSMConnection(EpollServer* epoll_server,
        SSLState* ssl_state,
        MemoryCache* memory_cache,
        FlipAcceptor* acceptor,
        std::string log_prefix);

    // TODO(mbelshe): Make these private.
    time_t last_read_time_;
    std::string server_ip_;
    std::string server_port_;

    EpollServer* epoll_server() override;
    OutputList* output_list() { return &output_list_; }
    MemoryCache* memory_cache() { return memory_cache_; }
    void ReadyToSend() override;
    void EnqueueDataFrame(DataFrame* df);

    int fd() const { return fd_; }
    bool initialized() const { return initialized_; }
    std::string client_ip() const { return client_ip_; }

    virtual void InitSMConnection(SMConnectionPoolInterface* connection_pool,
        SMInterface* sm_interface,
        EpollServer* epoll_server,
        int fd,
        std::string server_ip,
        std::string server_port,
        std::string remote_ip,
        bool use_ssl);

    void CorkSocket();
    void UncorkSocket();

    int Send(const char* data, int len, int flags);

    // EpollCallbackInterface interface.
    void OnRegistration(EpollServer* eps, int fd, int event_mask) override;
    void OnModification(int fd, int event_mask) override { }
    void OnEvent(int fd, EpollEvent* event) override;
    void OnUnregistration(int fd, bool replaced) override;
    void OnShutdown(EpollServer* eps, int fd) override;

    // NotifierInterface interface.
    void Notify() override { }

    void Cleanup(const char* cleanup);

    // Flag indicating if we should force spdy on all connections.
    static bool force_spdy() { return force_spdy_; }
    static void set_force_spdy(bool value) { force_spdy_ = value; }

private:
    // Decide if SPDY was negotiated.
    bool WasSpdyNegotiated(SpdyMajorVersion* version_negotiated);

    // Initialize the protocol interfaces we'll need for this connection.
    // Returns true if successful, false otherwise.
    bool SetupProtocolInterfaces();

    bool DoRead();
    bool DoWrite();
    bool DoConsumeReadData();
    void Reset();

    void HandleEvents();
    void HandleResponseFullyRead();

protected:
    friend std::ostream& operator<<(std::ostream& os, const SMConnection& c)
    {
        os << &c << "\n";
        return os;
    }

    SMConnection(EpollServer* epoll_server,
        SSLState* ssl_state,
        MemoryCache* memory_cache,
        FlipAcceptor* acceptor,
        std::string log_prefix);

private:
    int fd_;
    int events_;

    bool registered_in_epoll_server_;
    bool initialized_;
    bool protocol_detected_;
    bool connection_complete_;

    SMConnectionPoolInterface* connection_pool_;

    EpollServer* epoll_server_;
    SSLState* ssl_state_;
    MemoryCache* memory_cache_;
    FlipAcceptor* acceptor_;
    std::string client_ip_;

    RingBuffer read_buffer_;

    OutputList output_list_;
    SpdySM* sm_spdy_interface_;
    SMInterface* sm_http_interface_;
    SMInterface* sm_streamer_interface_;
    SMInterface* sm_interface_;
    std::string log_prefix_;

    size_t max_bytes_sent_per_dowrite_;

    SSL* ssl_;

    static bool force_spdy_;
};

} // namespace net

#endif // NET_TOOLS_FLIP_SERVER_SM_CONNECTION_H_
